#include <s32strm.h>
#include <s32file.h>

#include "StopwatchModel.h"

namespace
{

template <class T>
RReadStream& ReadAsInt64(RReadStream& aReadStream, T& aVal)
{
  TInt64 val = 0;
  aReadStream >> val;
  aVal = T(val);
  return aReadStream;
}

RWriteStream& operator<<(RWriteStream& aWriteStream, const TTimeIntervalMicroSeconds& aTime)
{
  return aWriteStream << aTime.Int64();
}

RReadStream& operator>>(RReadStream& aReadStream, TTimeIntervalMicroSeconds& aTime)
{
  return ReadAsInt64(aReadStream, aTime);
}


enum TMode 
{
  EModeUnknown = 0,
  EStopWatchStarted = 0xFA,
  EStopWatchStopped = 0xF5
};

TTimeIntervalMicroSeconds MicrosecondsPassed(const TTime& aTime)
{
  TTime time;
  time.UniversalTime();
  return time.MicroSecondsFrom(aTime);
}

void SaveL(RWriteStream& aWriteStream, TMode aMode, const TInt64& aValue, TArray<TSnapshot> aSnapshots)
{
  const TInt32 count = aSnapshots.Count();

  aWriteStream << static_cast<TUint8>(aMode) << aValue << count;
  for ( TInt i = 0; i < count; ++i )
    aWriteStream << aSnapshots[i];
}

void LoadL(RReadStream& aReadStream, TMode& aMode, TInt64& aValue, RArray<TSnapshot>& aSnapshots)
{
  aMode = static_cast<TMode>(aReadStream.ReadUint8L());
  aReadStream >> aValue;
  TInt32 count = aReadStream.ReadInt32L();

  RArray<TTimeIntervalMicroSeconds> snapshots;
  snapshots.ReserveL(count);
  CleanupClosePushL(snapshots);

  TTimeIntervalMicroSeconds tmpIntervalValue(0);
  for ( TInt i = 0; i < count; ++i )
  {
    aReadStream >> tmpIntervalValue;
    snapshots.AppendL(tmpIntervalValue);
  }

  CleanupStack::Pop(&snapshots);
  aSnapshots.Close();
  aSnapshots = snapshots;
}

}

CStopwatchModel::CStopwatchModel(MStopwatchModelObserver& aObserver)
  : iObserver(aObserver)
{}

CStopwatchModel::~CStopwatchModel()
{
  iSnapshots.Close();
}

CStopwatchModel* CStopwatchModel::NewLC(MStopwatchModelObserver& aObserver)
{
  CStopwatchModel* self = new (ELeave) CStopwatchModel(aObserver);
  CleanupStack::PushL(self);
  self->ConstructL();
  return self;
}

CStopwatchModel* CStopwatchModel::NewL(MStopwatchModelObserver& aObserver)
{
  CStopwatchModel* self = CStopwatchModel::NewLC(aObserver);
  CleanupStack::Pop(self);
  return self;
}

void CStopwatchModel::ConstructL()
{}

void CStopwatchModel::Start()
{
  iStartTime.UniversalTime();
  iStartTime -= iTimeDiff;
  iTimeDiff = 0;

  iIsStarted = ETrue;
  iObserver.StopwatchStarted();
}

void CStopwatchModel::Stop()
{
  iTimeDiff = MicrosecondsPassed(iStartTime);
  iStartTime = TTime(0);
  iIsStarted = EFalse;

  iObserver.StopwatchStopped();
}

void CStopwatchModel::Reset()
{
  iStartTime = TTime(0);
  iTimeDiff = TTimeIntervalMicroSeconds(0);
  iSnapshots.Reset();

  iObserver.StopwatchReset();
}

void CStopwatchModel::MakeSnapshotL()
{
  TTimeIntervalMicroSeconds snapshot = Value();

  if ( snapshot == 0 )
    User::Leave(KErrNotReady);

  //don't add a snapshot twice.
  if ( (iSnapshots.Count() > 0 && iSnapshots[iSnapshots.Count() - 1] == snapshot) )
    User::Leave(KErrAlreadyExists);

  iSnapshots.AppendL(snapshot);

  iObserver.StopwatchNewSnapshotValue(snapshot);
}

TTimeIntervalMicroSeconds CStopwatchModel::Value() const
{
  return iIsStarted ? MicrosecondsPassed(iStartTime) : iTimeDiff;
}

TArray<TSnapshot> CStopwatchModel::Snapshots() const
{
  return iSnapshots.Array();
}

TBool CStopwatchModel::IsRunning() const
{
  return iIsStarted;
}


void CStopwatchModel::LoadL(RFs& aFs, const TDesC& aFileName)
{
  RFileReadStream readStream;
  User::LeaveIfError(readStream.Open(aFs, aFileName, EFileRead | EFileStream));
  CleanupClosePushL(readStream);

  LoadL(readStream);

  CleanupStack::PopAndDestroy(&readStream);

  iObserver.StopwatchReset();
}

void CStopwatchModel::LoadL(RReadStream& aIn)
{
  TMode mode;
  RArray<TSnapshot> snapshots;
  TInt64 value;

  ::LoadL(aIn, mode, value, snapshots);

  iSnapshots.Close();
  iSnapshots = snapshots;

  TBool wasRunning = iIsStarted;
  if ( mode == EStopWatchStarted )
  {
    iIsStarted = ETrue;
    iStartTime = value; 
  }
  else
  {
    iIsStarted = EFalse;
    iTimeDiff = value;
  }

  if ( wasRunning != iIsStarted )
  {
    if ( iIsStarted )
      iObserver.StopwatchStarted();
    else
      iObserver.StopwatchStopped();
  }
  iObserver.StopwatchReset();
}

void CStopwatchModel::SaveL(RFs& aFs, const TDesC& aFileName)
{
  RFileWriteStream writeStream;
  User::LeaveIfError(writeStream.Replace(aFs, aFileName, EFileWrite | EFileStream));
  CleanupClosePushL(writeStream);

  SaveL(writeStream);

  CleanupStack::PopAndDestroy(&writeStream);
}

void CStopwatchModel::SaveL(RWriteStream& aOut)
{
  ::SaveL(aOut, iIsStarted ? EStopWatchStarted : EStopWatchStopped, iIsStarted ? iStartTime.Int64() : iTimeDiff.Int64(), Snapshots());
}

//void CStopWatchView::ExportDataL()
//{
//  _LIT(KExportedFileDirectory, "c:\\data\\others\\");
//  TFileName fileName;
//  {
//    HBufC* fileNameFormat = StringLoader::LoadLC(R_TIME_FORMAT, iCoeEnv);
//    TTime now;
//    now.UniversalTime();
//    now.FormatL(fileName, *fileNameFormat);
//    CleanupStack::PopAndDestroy(fileNameFormat);
//  }
//  fileName.Insert(0, KExportedFileDirectory());
//
//  RFileWriteStream outputFile;
//  User::LeaveIfError(outputFile.Create(iCoeEnv->FsSession(), fileName, EFileStreamText | EFileWrite));
//  CleanupClosePushL(outputFile);
//  ExportDataL(outputFile);
//  CleanupStack::PopAndDestroy(&outputFile);
//
//  HBufC* noteText = StringLoader::LoadLC(R_EXPORT_NOTE_FORMAT, fileName, iCoeEnv);
//  CAknInformationNote* note = new (ELeave) CAknInformationNote();
//  note->ExecuteLD(*noteText);
//  CleanupStack::PopAndDestroy(noteText);
//}

//void CStopWatchView::ExportDataL(RWriteStream& aOutput)
//{
//  _LIT(KNewLine, "\r\n");
//
//  aOutput << *StringLoader::LoadLC(R_EXPORT_PROLOG, iCoeEnv);
//  CleanupStack::PopAndDestroy();
//
//  aOutput << *StringLoader::LoadLC(R_EXPORT_FINAL_TIME);
//  CleanupStack::PopAndDestroy();
//
//  TBuf<100> timeBuf;
//  TTime(iTimeDiff.Int64()).FormatL(timeBuf, iTimeFormat);
//  aOutput << timeBuf;
//  aOutput << KNewLine();
//  
//  for ( TInt i = iSnapshots.Count() - 1; i >=0; --i )
//  {
//    aOutput << StringLoader::LoadLC(R_EXPORT_NTH_TIME, i + 1, iCoeEnv);
//    TTime(iSnapshots[i].Int64()).FormatL(timeBuf, iTimeFormat);
//    aOutput << timeBuf;
//    aOutput << KNewLine();
//  }
//}
